<?php
/**
 * PDO驱动抽象
 *
 * @package if
 * @subpackage module.db.drivers.pdo
 * @since 1.0
 */
abstract class IAbstractDbPdo extends IDbDriver {	
	/**
	 * Enter description here...
	 *
	 * @var PDO
	 */
	protected $pdo;
	protected $pdoType;
	private $dsn;
	
	function __construct(array $config) {
		parent::__construct($config);
		
		$this->pdoType = $this->pdoType();
		
		$this->dsn = $this->dsn();
		
		//连接
		$this->connect();
	}
	
	protected function dsn() {
		$config = $this->config;
		//do sth
		if (!isset($config["dsn"])) {
			$dsn = array();
			foreach ($config as $key=>$value) {
				if (!in_array($key, array("username", "password"))) {
					$dsn[] = $key . "=" . $value;
				}
			}
			$dsn = $this->pdoType . ":" . implode(";", $dsn);
			$this->_log("load pdo dsn " . $dsn);
			return $dsn;
		}
		else {
			return $config["dsn"];
		}
	}
	
	/**
	 * Enter description here...
	 *
	 * @return PDO
	 */
	protected function pdo() {
		return $this->pdo;
	}
	
	function nConnect($dsn = null) {
		if ($this->connected) {
			return;
		}
		if (!extension_loaded("pdo")) {
			throw new IDbException("pdo extension not be loaded", 0x100083);
		}
		if (!in_array($this->pdoType, PDO::getAvailableDrivers())) {
			throw new IDbException("pdo '{$this->pdoType}' is not currently installed", 0x100084);
		}
		//测试
		if ($dsn != null) {
			$this->dsn = $dsn;
		}
		$this->pdo = new PDO($this->dsn, $this->config("username"), $this->config("password"), $this->config("driver_options"));
		return $this->pdo;
	}
	
	function nClose() {
		$this->pdo = null;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $sql
	 * @param unknown_type $params
	 * @return IDbStatement
	 */
	function prepare($sql, $params = array()) {
		$stmt = new IDbStatement($this, $sql, $params);
		return $stmt;
	}
	
	function lastInsertId($name = null) {
		return $this->pdo->lastInsertId($name);
	}
	
	function beginTransaction() {
		$this->pdo->beginTransaction();
	}
	
	function commit() {
		$this->pdo->commit();
	}
	
	function rollback() {
		$this->pdo->rollBack();
	}
	
	function nQuote($data) {
		if (is_int($data) || is_float($data)) {
			return $data;
		}		
		if (is_null($data)) {
			return "NULL";
		}
		return $this->pdo->quote($data);
	}
	
	function exec($sql) {
		$this->_log("execute:{$sql}");
		$affectedRows = $this->pdo->exec($sql);
		$this->_findError($sql);
		return $affectedRows;
	}
	
	function nativeExec($sql) {
		$stmt = $this->pdo->query($sql);
		$this->_findError($sql);
		return $stmt;
	}
	
	function fetch($result, $fetchType = IDb::FETCH_BOTH, $columnIndex = 0) {
		$args = func_get_args();
		unset($args[0]);
		if ($fetchType != IDb::FETCH_COLUMN && $fetchType != IDb::FETCH_OBJ) {
			return call_user_func_array(array($result, "fetch"), array($args[1]));
		}
		else {
			return call_user_func_array(array($result, "fetch"), $args);
		}
	}
	
	function fetchAll($result, $fetchType = IDb::FETCH_BOTH, $columnIndex = 0) {
		if (!$result) {
			return array();
		}
		$args = func_get_args();
		if (!array_key_exists(1, $args)) {
			$args[1] = IDb::FETCH_BOTH;
		}
		unset($args[0]);
		if ($fetchType != IDb::FETCH_COLUMN && $fetchType != IDb::FETCH_OBJ) {
			return call_user_func_array(array($result, "fetchAll"), array($args[1]));
		}
		else {
			return call_user_func_array(array($result, "fetchAll"), $args);
		}
	}
	
	function error() {
		$code = $this->pdo->errorCode();
		if ($code === "00000") {
			return array();
		}
		$info = $this->pdo->errorInfo();
		$error = null;
		if (isset($info[1])) {
			$error = $info[1];
		}
		if (isset($info[2])) {
			$error = $info[2];
		}
		return array(
			"code" => $this->pdo->errorCode(),
			"message" => $error
		);
	}
	
	function rowCount($result) {
		return $result->rowCount();
	}
	
	private function _findError($sql = null) {
		$code = $this->pdo->errorCode();
		if ($code === "00000") {
			return false;
		}
		$info = $this->pdo->errorInfo();
		if (isset($info[2])) {
			throw new IDbException($info[2] . "<br/>\nSQL: " . $sql, $info[1]);
		}
		if (isset($info[1])) {
			throw new IDbException($info[1], 0x100085);
		}
	}
	
	function pdoType() {
		return $this->pdoType;
	}
	
	function move($result, $offset) {
		throw new IDbException("move method is not supported yet by pdo drivers", 0x100086);
	}	
}

?>